import resources from './resources';
import Avatar from './avatar';
import Matrix from './matrix';

export default class Game {
  soundOn = true;
  assets = null; // 缓存资源
  canvas = null; // 画布
  stage = null; // 舞台
  colNum = 7; // 默认列数
  rowNum = 8; // 默认行数
  avatarCount = 6; // 默认角色头像数
  loading = null;
  matrixArray = [];
  dropArray = [];
  needMove = []; // 需删除的元素
  moveCount = 0 ;
  remove = false;
  check = false;
  dropCount = 0 ;
  score = 0; // 游戏得分
  lock = false;

  loadingComplete = null;

  tickIsRunning = false;

  constructor(options) {
    const { canvas } = options;

    canvas.width = 750;
    canvas.height = 1206;
    this.canvas = canvas;
    this.stage = new createjs.Stage(this.canvas);
    this.stage.width = this.canvas.width;
    this.stage.height = this.canvas.height;
    this.timer = null;

  }



  /**
   * 初始化游戏
   * @returns {Game}
   */
  init() {

    this.clearStage();
    this.matrixArray = [];
    this.dropArray = [];
    this.needMove = [];
    this.moveCount = 0 ;
    this.lock = false;
    this.remove = false;
    this.check = false;
    this.loading = null;
    this.dropCount = 0 ;
    this.score = 0;
    if (!this.assets) {
      this.createLoading();
    } else {
      this.gameReady();
    }
    return this;
  }
  /**
   * 获取游戏资源
   */
  loadAssets(manifest) {
    this.assets = {};
    const que = new createjs.LoadQueue(true);
    que.setMaxConnections(5);
    que.on('fileload', this.handleFileLoad, this);
    que.on('progress', this.progressHandler, this);
    que.on('complete', this.gameReady, this);
    que.installPlugin(createjs.Sound);
    createjs.Sound.alternateExtensions = ['mp3'];
    que.loadManifest(manifest);
  }
  handleFileLoad(event) {
    this.assets[event.item.id] = event;
  }

  createLoading() {
    // loading文字
    this.loading = new createjs.Text('0%', 'bold 32px impact', '#EC603C');
    this.loading.maxWidth = 320;
    this.loading.textAlign = 'center';
    this.loading.x = this.stage.width / 2;
    this.loading.y = this.stage.height / 2;
    this.stage.addChild(this.loading);
    this.stage.update();

    createjs.Ticker.timingMode = createjs.Ticker.RAF;
    createjs.Ticker.setFPS(10);
    createjs.Ticker.addEventListener('tick', this.stage);
    this.loadAssets(resources.game);
  }

  progressHandler(event) {
    if (this.loading) {
      let percent = Math.floor(event.progress * 100);
      percent = percent > 100 ? 100 : percent;
      this.loading.text = percent + '%';
      this.stage.update();
    }
  }
  gameReady() {
    if (this.loading) {
      this.loading.alpha = 0;
    }
    this.startGame();
    this.loadSound('ready');
    this.loadingComplete && this.loadingComplete();

  }

  toggleBGM(status = true) {
    this.soundOn = status;
    if (status) {
      this.addBGM();
    } else {
      createjs.Sound.stop();
    }
  }
  
  addBGM() {
    createjs.Sound.stop();
    this.loadSound('bgsound', -1);
  }
  loadSound(id, loop = false, volume = 1) {
    // 声音添加全局开关
    if (this.soundOn) {
      createjs.Sound.play(id, createjs.Sound.INTERRUPT_NONE, 0, 0, loop, volume);
    }
  }
  clearStage() {
    this.stage.removeAllChildren();
    this.stage.removeAllEventListeners();
  }
  startGame() {
    createjs.Touch.enable(this.stage);
    this.clearStage();
    this.addBGM();
    this.createMatrix();
    this.setTick();
  }
  createMatrix() {
    this.matrixArray = [];
    // 初始化矩阵
    this.matrixArray = Matrix.createArray(this.rowNum, this.colNum);
    for (let col = 0; col < this.colNum; col++) {
      this.dropArray[col] = 0;
    }
    this.showMatrix();
  }
  showMatrix() {
    for (let y = 0; y < this.matrixArray.length; y++) {
      for (let x = 0; x < this.matrixArray[y].length; x++) {
        this.createAvatar(x, y);
      }
    }
  }

  createAvatar(x = 0, y = 0, dropY = -1) {
    let size = 86;
    const imagePath = this.assets.items.result;
    const spriteSheet = new createjs.SpriteSheet({
      images: [imagePath],
      frames: {
        width: size,
        height: size
      },
      animations: {
        'a1': [0],
        'a2': [1],
        'a3': [2],
        'a4': [3],
        'a5': [4],
        'a6': [5]
      }
    });
    const random = () => Math.ceil(Math.random() * this.avatarCount);
    const avatar = new Avatar(spriteSheet, this, {
      frame: random(),
      col: x,
      row: y,
      size,
    });
    if (dropY === -1) {
      avatar.x = x * size;
      avatar.y = y * size;
    } else {
      avatar.x = x * size;
      avatar.y = size * (y - dropY - 1);
    }

    if (dropY === -1) {
      this.matrixArray[y][x] = avatar;
      while (this.inLine(y, x)) {
        this.createAvatar(x, y);
      }
      this.stage.addChild(this.matrixArray[y][x]);
    } else {
      // todo 创建掉落
      this.matrixArray[y][x] = avatar;
      this.stage.addChild(avatar);

    }
  }

  changeItem(dir, item, type = '') {
    const animate = (t, d) => {
      t.moving = true;
      t.direction = d;
    };
    switch (dir) {
      case 1:
        this.dir = 1;
        const next = this.matrixArray[item.row][item.col + 1];
        animate(item, 'right');
        animate(next, 'left');

        this.matrixArray[item.row][item.col] = next;
        this.matrixArray[item.row][item.col + 1] = item;

        item.col += 1;
        next.col -= 1;

        this.dirThis = next;
        this.startThis = item;

        break;
      case 2:

        this.dir = 2;

        const prev = this.matrixArray[item.row][item.col - 1];

        animate(item, 'left');
        animate(prev, 'right');

        this.matrixArray[item.row][item.col] = prev;
        this.matrixArray[item.row][item.col - 1] = item;

        item.col -= 1;
        prev.col += 1;

        this.dirThis = prev;
        this.startThis = item;

        break;
      case 3:
        this.dir = 3;

        const down = this.matrixArray[item.row + 1][item.col];

        animate(item, 'down');
        animate(down, 'up');

        this.matrixArray[item.row][item.col] = down;
        this.matrixArray[item.row + 1][item.col] = item;

        item.row += 1;
        down.row -= 1;

        this.dirThis = down;
        this.startThis = item;

        break;
      case 4:
        this.dir = 4;

        const up = this.matrixArray[item.row - 1][item.col];

        animate(item, 'up');
        animate(up, 'down');

        this.matrixArray[item.row][item.col] = up;
        this.matrixArray[item.row - 1][item.col] = item;

        item.row -= 1;
        up.row += 1;

        this.dirThis = up;
        this.startThis = item;

        break;
    }

    if (type !== 'reset') {
      // 调用消除方法
      // console.log('processDisappear');
      this.markDisapear();
      // this.processDisappear();
    } else {
      setTimeout(() => {
        this.lock = false;
      });
    }

  }
  inLine(row, col, array) {
    return this.inX(row, col, array) || this.inY(row, col, array);
  }
  /**
   * 行判断
   * @param row
   * @param col
   * @param setArr
   * @returns {*}
   */
  inX(row, col, setArr) {
    setArr = setArr || this.matrixArray;
    let frame = setArr[row][col].frame;
    let removeX = [];
    let inXCount = 0;
    for (let x = 0; x < this.colNum; x++) {
      if (setArr[row][x].frame && setArr[row][x].frame === frame) {
        inXCount++;
        removeX.push([row, x]);
      } else if (inXCount < 3) {
        inXCount = 0;
        removeX = [];
      } else {
        break;
      }
    }
    if (inXCount >= 3) {
      return removeX;
    }
    return false;
  }

  /**
   * 列判断
   * @param row
   * @param col
   * @param setArr
   * @returns {*}
   */
  inY(row, col, setArr) {
    setArr = setArr || this.matrixArray;
    let frame = setArr[row][col].frame;
    let removeY = [];
    let inYCount = 0;
    for (let y = 0; y < this.rowNum; y++) {
      if (setArr[y][col].frame && setArr[y][col].frame === frame) {
        inYCount++;
        removeY.push([y, col]);
      } else if (inYCount < 3) {
        inYCount = 0;
        removeY = [];
      } else {
        break;
      }
    }
    if (inYCount >= 3) {
      return removeY;
    }
    return false;
  }

  static stop() {
    createjs.Ticker.paused = true;
  }

  /**
   * 标记要消除的块
   * @return {[type]} [description]
   */
  markDisapear() {
    // var markCount = 0;
    let remove = [];
    let tmpArr = [];
    // 获取被操作的元素相关的坐标
    tmpArr.push(this.inX(this.startThis.row, this.startThis.col));
    tmpArr.push(this.inY(this.startThis.row, this.startThis.col));
    tmpArr.push(this.inX(this.dirThis.row, this.dirThis.col));
    tmpArr.push(this.inY(this.dirThis.row, this.dirThis.col));
    for (let i = 0; i < tmpArr.length; i++) {
      if (tmpArr[i] && Array.isArray(tmpArr[i])) {
        remove = remove.concat(tmpArr[i]);
      }
    }
    remove = [...new Set([...remove])];
    if (remove.length > 0) {
      // 标记消除
      this.needMove = Matrix.uniqueRemove(remove);
    } else {
      // 标记还原
      this.needReset = true;
    }
  }

  /**
   * 设置舞台tick
   * @param {[type]} duration [description]
   */
  setTick() {
    if (createjs.Ticker.paused) {
      createjs.Ticker.paused = false;
    }
    if (!this.tickIsRunning) {
      this.tickIsRunning = true;
      // createjs.Ticker.removeEventListener('tick', this._ticker.bind(this));
      createjs.Ticker.setFPS(10);
      createjs.Ticker.addEventListener('tick', () => {
        // 还原
        if (this.reset) {
          this.reset = false;
          this.changeItem(this.dir, this.dirThis, 'reset');
        }

        if (this.dropCount < 0) {
          this.dropCount = 0;
          this.drop = false;
        }

        // 删除
        if (this.remove && this.needMove.length > 0) {
          this.remove = false;
          this.moveItems(this.needMove);
        }


        /*if (this.dropCount !== 0) {

        }*/
        //console.log('_this.dropCount', this.dropCount, this.check);
        if (this.check && this.dropCount === 0) {
          this.checkAll();
        }


        // this.stage.update();
      });
    }
  }

  /**
   * 删除元素
   * @param  {[type]} removeArr [description]
   * @return {[type]}           [description]
   */
  moveItems(removeArr) {
    // this.dropCount = 0;
    let cols = [];
    let colCount = 0;
    let minRow = 0;
    this.moveCount = removeArr.length;

    for (let i = 0; i < removeArr.length; i++) {
      // 需要消除的元素,以及其横纵坐标
      let item = this.matrixArray[removeArr[i][0]][removeArr[i][1]];
      let col = removeArr[i][1]; // 列
      let row = removeArr[i][0]; // 行
      let tmpDrop = [];

      // 一列只判断一次
      if (cols.indexOf(col) === -1) {
        // 如果这一列出现未出现过消除的情况
        // minRow = removeArr[i][0]; // minRow = 行坐标
        colCount = 0; // 这一列需要消除的个数
        cols.push(col); // 标记这一列
        let temp = [];
        // 判断同列消除个数
        // let dropLengthTmp = 0;
        //console.log(`${JSON.stringify(removeArr)}`);



        let array = [];
        for (let j = 0; j < removeArr.length; j++) {
          if (removeArr[j][1] === col) {
            array.push(removeArr[j]);
          }
        }
        array.sort((a, b) => {
          return b[0] - a[0];
        });

        minRow = array[0][0]; // minRow = 行坐标
        let dropLengthTmp = 0;
        for (let i = 0; i < array.length; i++) {
          colCount++;
          if (minRow - array[i][0] === 0 || minRow - array[i][0] === 1) {
            dropLengthTmp += 1;
          } else {
            temp.push({minRow, colCount: colCount - 1, dropLengthTmp});
            dropLengthTmp = 1;
          }
          minRow = array[i][0];
        }

        temp.push({minRow, colCount, dropLengthTmp});
        let start = 0;
        this.dropArray[col] = [];
        while (temp.length > 0) {
          // console.log(JSON.stringify(temp));
          let t = temp.pop();
          // console.log(start, t.minRow);
          for (let x = start; x < t.minRow; x++) { // x是从0开始的二维数组的下标
            let dropitem = this.matrixArray[x][col];
            tmpDrop.push(dropitem);// 这一列要下落的元素
            dropitem.row = x + t.colCount; // 这个是这个要下落元素的新的row坐标，能否在这里直接进行确定
            this.dropCount++;
          }
          let length = ~~tmpDrop.length;
          this.dropArray[col].push({minRow: t.minRow, dropLength: length});

          start = ~~(t.minRow) + t.dropLengthTmp;
        }
        temp = [];
        // this.dropArray[col] = tmpDrop.length; // 下落的数量
      }
      // console.log(`col,this.dropArray[col]:${col},${JSON.stringify(this.dropArray[col])}`);
      item.disappear = true;// 驱动变更

    }


    this.needMove = [];
  }

  updateArr(col, row) {
    if (typeof this.matrixArray[row][col].frame === 'undefined' || this.matrixArray[row][col].frame === '') {
      let obj = this.dropArray[col];
      // console.log(obj);
      // let newY = row - this.dropArray[col];
      let dropLength = 0;
      for (let j = 0; j < obj.length; j++) {
        // console.log(row, obj[j].minRow);
        dropLength = row >= obj[j].minRow ? obj[j].dropLength : dropLength;
      }
      // console.log(dropLength);
      this.createAvatar(col, row - dropLength, dropLength);
      this.dropCount++;  // todo ???
      this.moveCount--;
    }

    // 消除并全部更新后，自动判断一次
    if (this.moveCount <= 0) {
      this.check = true;
    }
  }

  /**
   * 校验全局，检查是否死局或需要自动消除
   * @return {[type]} [description]
   */
  checkAll() {
    // console.log('checkAll');
    this.check = false;
    let hasLine = false;
    for (let y = 0; y < this.rowNum; y++) {
      for (let x = 0; x < this.colNum; x++) {
        let remove = [];
        let tmpArr = [];
        tmpArr.push(this.inX(y, x));
        tmpArr.push(this.inY(y, x));

        for (let i = 0; i < tmpArr.length; i++) {

          if (tmpArr[i] && Array.isArray(tmpArr[i])) {

            remove = remove.concat(tmpArr[i]);
          }
        }
        // remove = $.unique(remove);

        if (remove.length > 0) {
          // this.log("x:" + x + "y:" + y + ">>remove");
          // this.log(remove);
          this.needMove = this.needMove.concat(remove);

          hasLine = true;
          break;
        }
      }
    }
    // hasLine = true;
    // 自动消除
    if (hasLine) {
      // this.needMove = [[2,2],[2,3],[2,4],[3,2],[4,2]];
      this.needMove = Matrix.uniqueRemove(this.needMove);
      this.remove = true;
    } else { // 判断是否死局
      // if (!this.checkIsLive()) {
      //     this.log("死局");
      //     //this.creatMatrix();
      // }
      setTimeout(() => {
        this.lock = false;
      });
    }
  }
}
